1   /*
2    * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.
8    *
9    * This code is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12   * version 2 for more details (a copy is included in the LICENSE file that
13   * accompanied this code).
14   *
15   * You should have received a copy of the GNU General Public License version
16   * 2 along with this work; if not, write to the Free Software Foundation,
17   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18   *
19   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20   * or visit www.oracle.com if you need additional information or have any
21   * questions.
22   */
23  
24  /*
25   * @test
26   * @bug 5083253
27   * @run main CTSMode
28   * @summary Verify that CTR mode works as expected
29   * @author Valerie Peng
30   */
31  
32  import java.util.*;
33  import java.security.AlgorithmParameters;
34  import javax.crypto.*;
35  import javax.crypto.spec.*;
36  
37  public class CTSMode {
38  
39      private final static byte[] toByteArray(String s) {
40          char[] c = s.toCharArray();
41          byte[] t = new byte[c.length / 2];
42          int n = 0;
43          int d1 = -1;
44          int d2 = -1;
45          for (int i = 0; i < c.length; i++) {
46              char ch = c[i];
47              if (d1 == -1) {
48                  d1 = Character.digit(ch, 16);
49              } else {
50                  d2 = Character.digit(ch, 16);
51                  if (d2 != -1) {
52                      t[n++] = (byte)((d1 << 4) | d2);
53                      d1 = -1;
54                      d2 = -1;
55                  }
56              }
57          }
58          if (d1 != -1) {
59              throw new RuntimeException();
60          }
61          if (n == t.length) {
62              return t;
63          }
64          byte[] b = new byte[n];
65          System.arraycopy(t, 0, b, 0, n);
66          return b;
67      }
68  
69      private final static SecretKey KEY1 =
70          new SecretKeySpec(toByteArray("636869636b656e207465726979616b69"),
71                            "AES");
72  
73      private final static IvParameterSpec IV1 =
74          new IvParameterSpec(new byte[16]);
75  
76      /*
77       * Use test vectors, i.e. PLAIN1 and CIPHER1, from the appendix B
78       * of RFC 3962 "Advanced Encryption Standard (AES) Encryption for
79       * Kerberos 5".
80       */
81      private final static byte[][] PLAIN1 = {
82          toByteArray("4920776f756c64206c696b652074686520"),
83          toByteArray("4920776f756c64206c696b6520746865" +
84                      "2047656e6572616c20476175277320"),
85          toByteArray("4920776f756c64206c696b6520746865" +
86                      "2047656e6572616c2047617527732043"),
87          toByteArray("4920776f756c64206c696b6520746865" +
88                      "2047656e6572616c2047617527732043" +
89                      "6869636b656e2c20706c656173652c"),
90          toByteArray("4920776f756c64206c696b6520746865" +
91                      "2047656e6572616c2047617527732043" +
92                      "6869636b656e2c20706c656173652c20"),
93          toByteArray("4920776f756c64206c696b6520746865" +
94                      "2047656e6572616c2047617527732043" +
95                      "6869636b656e2c20706c656173652c20" +
96                      "616e6420776f6e746f6e20736f75702e")
97      };
98      private final static byte[][] CIPHER1 = {
99          toByteArray("c6353568f2bf8cb4d8a580362da7ff7f97"),
100         toByteArray("fc00783e0efdb2c1d445d4c8eff7ed22" +
101                     "97687268d6ecccc0c07b25e25ecfe5"),
102         toByteArray("39312523a78662d5be7fcbcc98ebf5a8" +
103                     "97687268d6ecccc0c07b25e25ecfe584"),
104         toByteArray("97687268d6ecccc0c07b25e25ecfe584" +
105                     "b3fffd940c16a18c1b5549d2f838029e" +
106                     "39312523a78662d5be7fcbcc98ebf5"),
107         toByteArray("97687268d6ecccc0c07b25e25ecfe584" +
108                     "9dad8bbb96c4cdc03bc103e1a194bbd8" +
109                     "39312523a78662d5be7fcbcc98ebf5a8"),
110         toByteArray("97687268d6ecccc0c07b25e25ecfe584" +
111                     "39312523a78662d5be7fcbcc98ebf5a8" +
112                     "4807efe836ee89a526730dbc2f7bc840" +
113                     "9dad8bbb96c4cdc03bc103e1a194bbd8"),
114     };
115 
116     private final static byte[] IV2_SRC =
117         toByteArray("11223344556677880011223344556677");
118 
119     private final static String[] ALGORITHMS2 = {
120         "DES", "DESede", "Blowfish", "AES"
121     };
122     private final static int[] KEYSIZES2 = {
123         8, 24, 16, 16
124     };
125 
126     private static String toString(byte[] b) {
127         StringBuffer sb = new StringBuffer(b.length * 3);
128         for (int i = 0; i < b.length; i++) {
129             int k = b[i] & 0xff;
130             if (i != 0) {
131                 sb.append(':');
132             }
133             sb.append(Character.forDigit(k >> 4, 16));
134             sb.append(Character.forDigit(k & 0xf, 16));
135         }
136         return sb.toString();
137     }
138 
139     public static void main(String[] args) throws Exception {
140         test1();
141         test2();
142         test3();
143    }
144 
145     /**
146      * Test with the test vectors and see if results match.
147      */
148     private static void test1() throws Exception {
149         for (int i = 0; i < PLAIN1.length; i++) {
150             String algo = KEY1.getAlgorithm();
151             int MAX_KEYSIZE = Cipher.getMaxAllowedKeyLength(algo);
152             if (KEY1.getEncoded().length > MAX_KEYSIZE) {
153                 // skip tests using keys whose length exceeds
154                 // what's configured in jce jurisdiction policy files.
155                 continue;
156             }
157             System.out.println("Running test1_" + i +  " (" + algo + ")");
158             Cipher cipher = Cipher.getInstance(algo+ "/CTS/NoPadding",
159                                                "SunJCE");
160             byte[] plainText = PLAIN1[i];
161             byte[] cipherText = CIPHER1[i];
162             cipher.init(Cipher.ENCRYPT_MODE, KEY1, IV1);
163             byte[] enc = cipher.doFinal(plainText);
164             if (Arrays.equals(cipherText, enc) == false) {
165                 System.out.println("plain:  " + toString(plainText));
166                 System.out.println("cipher: " + toString(cipherText));
167                 System.out.println("actual: " + toString(enc));
168                 throw new RuntimeException("Encryption failure for test " + i);
169             }
170             cipher.init(Cipher.DECRYPT_MODE, KEY1, IV1);
171             byte[] dec = cipher.doFinal(cipherText);
172             if (Arrays.equals(plainText, dec) == false) {
173                 System.out.println("cipher: " + toString(cipherText));
174                 System.out.println("plain:  " + toString(plainText));
175                 System.out.println("actual: " + toString(enc));
176                 throw new RuntimeException("Decryption failure for test " + i);
177             }
178         }
179     }
180 
181     /**
182      * Test with a combination of update/doFinal calls and make
183      * sure that same data is recovered after decryption.
184      */
185     private static void test2() throws Exception {
186         for (int i = 0; i < ALGORITHMS2.length; i++) {
187             String algo = ALGORITHMS2[i];
188             System.out.println("Running test2_" + i +  " (" + algo + ")");
189             int keySize = KEYSIZES2[i];
190             int MAX_KEYSIZE = Cipher.getMaxAllowedKeyLength(algo);
191             if (keySize > MAX_KEYSIZE) {
192                 // skip tests using keys whose length exceeds
193                 // what's configured in jce jurisdiction policy files.
194                 continue;
195             }
196             Cipher cipher =
197                 Cipher.getInstance(algo+"/CTS/NoPadding", "SunJCE");
198             int blockSize = cipher.getBlockSize();
199             SecretKeySpec key = new SecretKeySpec(new byte[keySize], algo);
200             // Make sure encryption works for inputs with valid length
201             byte[] plainText = PLAIN1[3];
202             cipher.init(Cipher.ENCRYPT_MODE, key);
203             byte[] cipherText = new byte[plainText.length];
204             int firstPartLen = blockSize + 1;
205             int processed1 = cipher.update(plainText, 0, firstPartLen,
206                                            cipherText, 0);
207             int processed2 = cipher.doFinal(plainText, firstPartLen,
208                                             plainText.length-firstPartLen,
209                                             cipherText, processed1);
210             AlgorithmParameters params = cipher.getParameters();
211             if ((processed1 + processed2) != plainText.length) {
212                 System.out.println("processed1 = " + processed1);
213                 System.out.println("processed2 = " + processed2);
214                 System.out.println("total length = " + plainText.length);
215                 throw new RuntimeException("Encryption failure for test " + i);
216             }
217             // Make sure IllegalBlockSizeException is thrown for inputs
218             // with less-than-a-block length
219             try {
220                 cipher.doFinal(new byte[blockSize-1]);
221                 throw new RuntimeException("Expected IBSE is not thrown");
222             } catch (IllegalBlockSizeException ibse) {
223             }
224             // Make sure data is encrypted as in CBC mode for inputs
225             // which is exactly one block long
226             IvParameterSpec iv2 = new IvParameterSpec(IV2_SRC, 0, blockSize);
227             cipher.init(Cipher.ENCRYPT_MODE, key, iv2);
228             Cipher cipher2 =
229                 Cipher.getInstance(algo+"/CBC/NoPadding", "SunJCE");
230             cipher2.init(Cipher.ENCRYPT_MODE, key, iv2);
231 
232             byte[] eout = cipher.doFinal(IV2_SRC, 0, blockSize);
233             byte[] eout2 = cipher2.doFinal(IV2_SRC, 0, blockSize);
234             if (!Arrays.equals(eout, eout2)) {
235                 throw new RuntimeException("Different encryption output " +
236                                            "for CBC and CTS");
237             }
238             // Make sure decryption works for inputs with valid length
239             cipher.init(Cipher.DECRYPT_MODE, key, params);
240             byte[] recoveredText =
241                 new byte[cipher.getOutputSize(cipherText.length)];
242             processed1 = cipher.update(cipherText, 0, firstPartLen,
243                                        recoveredText, 0);
244             processed2 = cipher.update(cipherText, firstPartLen,
245                                        cipherText.length-firstPartLen,
246                                        recoveredText, processed1);
247             int processed3 =
248                 cipher.doFinal(recoveredText, processed1+processed2);
249             if ((processed1 + processed2 + processed3) != plainText.length) {
250                 System.out.println("processed1 = " + processed1);
251                 System.out.println("processed2 = " + processed2);
252                 System.out.println("processed3 = " + processed3);
253                 System.out.println("total length = " + plainText.length);
254                 throw new RuntimeException("Decryption failure for test " + i);
255             }
256             if (Arrays.equals(plainText, recoveredText) == false) {
257                 System.out.println("plain:  " + toString(plainText));
258                 System.out.println("recovered: " + toString(recoveredText));
259                 throw new RuntimeException("Decryption failure for test " + i);
260             }
261             // Make sure IllegalBlockSizeException is thrown for inputs
262             // with less-than-a-block length
263             try {
264                 cipher.doFinal(new byte[blockSize-1]);
265                 throw new RuntimeException("Expected IBSE is not thrown");
266             } catch (IllegalBlockSizeException ibse) {
267             }
268             // Make sure data is decrypted as in CBC mode for inputs
269             // which is exactly one block long
270             cipher.init(Cipher.DECRYPT_MODE, key, iv2);
271             cipher2.init(Cipher.DECRYPT_MODE, key, iv2);
272             byte[] dout = cipher.doFinal(eout);
273             byte[] dout2 = cipher2.doFinal(eout2);
274             if (!Arrays.equals(dout, dout2)) {
275                 throw new RuntimeException("Different decryption output " +
276                                            "for CBC and CTS");
277             }
278         }
279     }
280 
281     /**
282      * Test with a shortBuffer and see if encryption/decryption
283      * still works correctly afterwards.
284      */
285     private static void test3() throws Exception {
286         // Skip PLAIN1[0, 1, 2] due to their lengths
287         for (int i = 3; i < PLAIN1.length; i++) {
288             String algo = KEY1.getAlgorithm();
289             System.out.println("Running test3_" + i +  " (" + algo + ")");
290             int MAX_KEYSIZE = Cipher.getMaxAllowedKeyLength(algo);
291             if (KEY1.getEncoded().length > MAX_KEYSIZE) {
292                 // skip tests using keys whose length exceeds
293                 // what's configured in jce jurisdiction policy files.
294                 continue;
295             }
296             Cipher cipher =
297                 Cipher.getInstance(algo+ "/CTS/NoPadding", "SunJCE");
298             byte[] plainText = PLAIN1[i];
299             byte[] cipherText = CIPHER1[i];
300             byte[] enc = new byte[plainText.length];
301             cipher.init(Cipher.ENCRYPT_MODE, KEY1, IV1);
302             int halfInput = plainText.length/2;
303             int processed1 = cipher.update(plainText, 0, halfInput,
304                                            enc, 0);
305             try {
306                 cipher.doFinal(plainText, halfInput,
307                                plainText.length-halfInput,
308                                new byte[1], 0);
309                 throw new RuntimeException("Expected Exception is not thrown");
310             } catch (ShortBufferException sbe) {
311                 // expected exception thrown; retry
312                 int processed2 =
313                     cipher.doFinal(plainText, halfInput,
314                                    plainText.length-halfInput,
315                                    enc, processed1);
316                 if ((processed1 + processed2) != enc.length) {
317                     System.out.println("processed1 = " + processed1);
318                     System.out.println("processed2 = " + processed2);
319                     System.out.println("total length = " + enc.length);
320                     throw new RuntimeException("Encryption length check " +
321                                                "failed");
322                 }
323             }
324             if (Arrays.equals(cipherText, enc) == false) {
325                 System.out.println("plain:  " + toString(plainText));
326                 System.out.println("cipher: " + toString(cipherText));
327                 System.out.println("actual: " + toString(enc));
328                 throw new RuntimeException("Encryption failure for test " + i);
329             }
330             cipher.init(Cipher.DECRYPT_MODE, KEY1, IV1);
331             byte[] dec =
332                 new byte[cipher.getOutputSize(cipherText.length)];
333             processed1 = cipher.update(cipherText, 0, halfInput,
334                                        dec, 0);
335             try {
336                 cipher.update(cipherText, halfInput,
337                               cipherText.length-halfInput,
338                               new byte[1], 0);
339                 throw new RuntimeException("Expected Exception is not thrown");
340             } catch (ShortBufferException sbe) {
341                 // expected exception thrown; retry
342                 int processed2 = cipher.update(cipherText, halfInput,
343                                                cipherText.length-halfInput,
344                                                dec, processed1);
345                 int processed3 =
346                     cipher.doFinal(dec, processed1+processed2);
347                 if ((processed1 + processed2 + processed3) != dec.length) {
348                     System.out.println("processed1 = " + processed1);
349                     System.out.println("processed2 = " + processed2);
350                     System.out.println("processed3 = " + processed3);
351                     System.out.println("total length = " + dec.length);
352                     throw new RuntimeException("Decryption length check " +
353                                                "failed");
354                 }
355             }
356             if (Arrays.equals(plainText, dec) == false) {
357                 System.out.println("cipher: " + toString(cipherText));
358                 System.out.println("plain:  " + toString(plainText));
359                 System.out.println("actualD: " + toString(dec));
360                 throw new RuntimeException("Decryption failure for test " + i);
361             }
362         }
363     }
364 }